% Default Stan parameters and validators. Should only contain
% parameters that are valid inputs to Stan cmd-line!
% validator can be
% 1) function handle
% 2) 1x2 cell array of cells, input to validateattributes first element is classes,
% second is attributes
% 3) cell array of strings representing valid arguments

function [params,valid] = stan_params(ver)
   if nargin == 0
      ver = [2 0 1]; % default [major minor patch]
   end

   if mstan.check_ver(ver,'2.1.0') % Stan 2.1+
      adapt_params = struct(...
         'engaged',true,...
         'gamma',0.05,...
         'delta',0.8,...
         'kappa',0.75,...
         't0',10,...
         'init_buffer',75,...
         'term_buffer',50,...
         'window',25);
      adapt_valid = struct(...
         'engaged',{{{'logical'} {'scalar'}}},...
         'gamma',{{{'numeric'} {'scalar','>',0}}},...
         'delta',{{{'numeric'} {'scalar','>',0}}},...
         'kappa',{{{'numeric'} {'scalar','>',0}}},...
         't0',{{{'numeric'} {'scalar','>',0}}},...
         'init_buffer',{{{'numeric'} {'scalar'}}},...
         'term_buffer',{{{'numeric'} {'scalar'}}},...
         'window',{{{'numeric'} {'scalar'}}});
      output_params = struct(...
         'file','output.csv',...
         'diagnostic_file','',...
         'refresh',100);
      output_valid = struct(...
         'file',@isstr,...
         'diagnostic_file',@isstr,...
         'refresh',{{{'numeric'} {'scalar','>',0}}});
   else % Stan 2.0.1
      adapt_params = struct(...
         'engaged',true,...
         'gamma',0.05,...
         'delta',0.65,...
         'kappa',0.75,...
         't0',10);
      adapt_valid = struct(...
         'engaged',{{{'logical'} {'scalar'}}},...
         'gamma',{{{'numeric'} {'scalar','>',0}}},...
         'delta',{{{'numeric'} {'scalar','>',0}}},...
         'kappa',{{{'numeric'} {'scalar','>',0}}},...
         't0',{{{'numeric'} {'scalar','>',0}}});
      output_params = struct(...
         'file','output.csv',...
         'append_sample',false,...
         'diagnostic_file','',...
         'append_diagnostic',false,...
         'refresh',100);
      output_valid = struct(...
         'file',@isstr,...
         'append_sample',{{{'logical'} {'scalar'}}},...
         'diagnostic_file',@isstr,...
         'append_diagnostic',{{{'logical'} {'scalar'}}},...
         'refresh',{{{'numeric'} {'scalar','>',0}}});
   end

   params.sample = struct(...
                         'num_samples',1000,...
                         'num_warmup',1000,...
                         'save_warmup',false,...
                         'thin',1,...
                         'adapt',adapt_params,...
                         'algorithm','hmc',...
                         'hmc',struct(...
                                      'engine','nuts',...
                                      'static',struct('int_time',2*pi),...
                                      'nuts',struct('max_depth',10),...
                                      'metric','diag_e',...
                                      'stepsize',1,...
                                      'stepsize_jitter',0));
   valid.sample = struct(...
                         'num_samples',{{{'numeric'} {'scalar','>=',0}}},...
                         'num_warmup',{{{'numeric'} {'scalar','>=',0}}},...
                         'save_warmup',{{{'logical'} {'scalar'}}},...
                         'thin',{{{'numeric'} {'scalar','>',0}}},...
                         'adapt',adapt_valid,...
                         'algorithm',{{'hmc' 'fixed_param'}},...
                         'hmc',struct(...
                                      'engine',{{'static' 'nuts'}},...
                                      'static',struct('int_time',{{{'numeric'} {'scalar','>',0}}}),...
                                      'nuts',struct('max_depth',{{{'numeric'} {'scalar','>',0}}}),...
                                      'metric',{{'unit_e' 'diag_e' 'dense_e'}},...
                                      'stepsize',{{{'numeric'} {'scalar','>',0}}},...
                                      'stepsize_jitter',{{{'numeric'} {'scalar','>=',0,'<=',1}}}));

   params.optimize = struct(...
                           'algorithm','lbfgs',...
                           'nesterov',struct(...
                                             'stepsize',1),...
                           'bfgs',struct(...
                                         'init_alpha',0.001,...
                                         'tol_obj',1e-12,...
                                         'tol_rel_obj',1e4,...
                                         'tol_grad',1e-8,...
                                         'tol_rel_grad',1e7,...
                                         'tol_param',1e-8),...
                           'lbfgs',struct(...
                                         'init_alpha',0.001,...
                                         'tol_obj',1e-12,...
                                         'tol_rel_obj',1e4,...
                                         'tol_grad',1e-8,...
                                         'tol_rel_grad',1e7,...
                                         'tol_param',1e-8,...
                                         'history_size',5),...
                           'iter',2000,...
                           'save_iterations',false);

   valid.optimize = struct(...
                           'algorithm',{{'lbfgs' 'bfgs' 'nesterov' 'newton'}},...
                           'nesterov',struct(...
                                             'stepsize',{{{'numeric'} {'scalar','>',0}}}),...
                           'bfgs',struct(...
                                         'init_alpha',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_obj',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_rel_obj',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_grad',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_rel_grad',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_param',{{{'numeric'} {'scalar','>',0}}}),...
                           'lbfgs',struct(...
                                         'init_alpha',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_obj',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_rel_obj',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_grad',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_rel_grad',{{{'numeric'} {'scalar','>',0}}},...
                                         'tol_param',{{{'numeric'} {'scalar','>',0}}},...
                                         'history_size',{{{'numeric'} {'scalar','>',0}}}),...
                           'iter',{{{'numeric'} {'scalar','>',0}}},...
                           'save_iterations',{{{'logical'} {'scalar'}}});

   params.variational = struct(...
                         'algorithm','meanfield',...
                         'iter',10000,...
                         'grad_samples',1,...
                         'elbo_samples',100,...
                         'eta',1,...
                         'adapt',struct(...
                                       'engaged',true,...
                                       'iter',50),...
                         'tol_rel_obj',0.01,...
                         'eval_elbo',100,...
                         'output_samples',1000);
                      
   valid.variational = struct(...
                         'algorithm',{{'meanfield' 'fullrank'}},...
                         'iter',{{{'numeric'} {'scalar','>',0}}},...
                         'grad_samples',{{{'numeric'} {'scalar','>',0}}},...
                         'elbo_samples',{{{'numeric'} {'scalar','>',0}}},...
                         'eta',{{{'numeric'} {'scalar','>',0}}},...
                         'adapt',struct(...
                                       'engaged',{{{'logical'} {'scalar'}}},...
                                       'iter',{{{'numeric'} {'scalar','>',0}}}),...
                         'tol_rel_obj',{{{'numeric'} {'scalar','>',0}}},...
                         'eval_elbo',{{{'numeric'} {'scalar','>',0}}},...
                         'output_samples',{{{'numeric'} {'scalar','>',0}}});
                                   
   params.diagnose = struct(...
                           'test','gradient');
   valid.diagnose = struct(...
                           'test',{{{'gradient'}}});

   params.id = 1;
   
   params.data = struct('file','');
   valid.data = struct('file',@isstr);
   params.init = 2;
   valid.init = {{'numeric'} {'scalar'}}; % shitty validator
   params.random = struct('seed',-1);
   valid.random = struct('seed',{{{'numeric'} {'scalar'}}});

   params.output = output_params;
   valid.output = output_valid;
end
